﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace nRoute.Components.Collections
{
    public class LazyDictionary<TKey, TValue>
        : IDictionary<TKey, TValue>
    {
        private Dictionary<TKey, TValue> _innerDictonary;

#region IDictonary Related

        public void Add(TKey key, TValue value)
        {
            EnsureDictonaryExists();
            _innerDictonary.Add(key, value);
        }

        public bool ContainsKey(TKey key)
        {
            return (_innerDictonary == null) ? false : _innerDictonary.ContainsKey(key);
        }

        public ICollection<TKey> Keys
        {
            get { return (_innerDictonary == null) ? (ICollection<TKey>)Enumerable.Empty<TKey>().ToList() : _innerDictonary.Keys; }
        }

        public bool Remove(TKey key)
        {
            return (_innerDictonary == null) ? false : _innerDictonary.Remove(key);
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            value = default(TValue);
            return (_innerDictonary == null) ? false : _innerDictonary.TryGetValue(key, out value);
        }

        public ICollection<TValue> Values
        {
            get { return (_innerDictonary == null) ? (ICollection<TValue>)Enumerable.Empty<TValue>().ToList() : _innerDictonary.Values; }
        }

        public TValue this[TKey key]
        {
            get
            {
                if (_innerDictonary == null) 
                {
                    throw new KeyNotFoundException();
                }
                return _innerDictonary[key];
            }
            set
            {
                EnsureDictonaryExists();
                _innerDictonary[key] = value;
            }
        }

        public void Add(KeyValuePair<TKey, TValue> item)
        {
            EnsureDictonaryExists();
            ((IDictionary<TKey, TValue>)_innerDictonary).Add(item);
        }

        public void Clear()
        {
            if (_innerDictonary == null) return;
            _innerDictonary.Clear();
        }

        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            if (_innerDictonary == null) return false;
            return _innerDictonary.Contains(item);
        }

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            if (_innerDictonary == null) return;
            ((IDictionary<TKey, TValue>)_innerDictonary).CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return _innerDictonary == null ? 0 : _innerDictonary.Count; }
        }

        public bool IsReadOnly
        {
            get { return _innerDictonary == null ? false : ((IDictionary<TKey, TValue>)_innerDictonary).IsReadOnly; }
        }

        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            return (_innerDictonary == null) ? false : ((IDictionary<TKey, TValue>)_innerDictonary).Remove(item);
        }

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return _innerDictonary == null ? Enumerable.Empty<KeyValuePair<TKey, TValue>>().GetEnumerator() : _innerDictonary.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

#endregion

#region Helpers

        private void EnsureDictonaryExists()
        {
            if (_innerDictonary == null)
            {
                lock (this)
                {
                    if (_innerDictonary == null) _innerDictonary = new Dictionary<TKey, TValue>();
                }
            }
        }

#endregion

    }
}
